#include "pch.hpp"
#include "D3D11BlendState.hpp"
#include "D3D11Exception.hpp"

using namespace Engine;
using namespace Engine::Video;




D3D11BlendState::D3D11BlendState(const notify& destroyedFn)
	: m_changed(false)
	, m_destroyed(destroyedFn)
	, m_srcBlend(Blend::One)
	, m_destBlend(Blend::Zero)
	, m_blendOp(BlendOp::Add)
{}
///////////////////////////////////////////////////////////////////////////////////////////////////

void D3D11BlendState::post_create(ptr _this)
{
	m_this = _this;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

D3D11BlendState::~D3D11BlendState()
{
	m_destroyed(m_this);
}
///////////////////////////////////////////////////////////////////////////////////////////////////




void D3D11BlendState::setSrcBlend(Blend::Type blend)
{
	m_srcBlend = blend;
	m_changed = true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void D3D11BlendState::setDestBlend(Blend::Type blend)
{
	m_destBlend = blend;
	m_changed = true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void D3D11BlendState::setBlendOp(BlendOp::Type blendOp)
{
	m_blendOp = blendOp;
	m_changed = true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////




const ID3D11BlendStatePtr& D3D11BlendState::apply(const ID3D11DevicePtr& device, D3D11DeviceContext& context)
{
	update(device, context);

	return m_blendState;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void D3D11BlendState::update(const ID3D11DevicePtr& device, D3D11DeviceContext& context)
{
	if(!m_changed)
		return;

	D3D11_BLEND_DESC desc;
	ZeroMemory(&desc, sizeof(desc));
	desc.IndependentBlendEnable = false;
	desc.RenderTarget[0].BlendEnable = true;
	desc.RenderTarget[0].SrcBlend    = fromBlend(m_srcBlend);
	desc.RenderTarget[0].DestBlend   = fromBlend(m_destBlend);
	desc.RenderTarget[0].BlendOp     = fromBlendOp(m_blendOp);
	desc.RenderTarget[0].SrcBlendAlpha = fromBlend(Blend::One);
	desc.RenderTarget[0].DestBlendAlpha = fromBlend(Blend::Zero);
	desc.RenderTarget[0].BlendOpAlpha   = fromBlendOp(BlendOp::Add);
	desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;

	ID3D11BlendState* blendState = NULL;
	D3D11_API(device->CreateBlendState(&desc, &blendState));
	m_blendState = ID3D11BlendStatePtr(blendState);

	m_changed = false;
}
///////////////////////////////////////////////////////////////////////////////////////////////////




D3D11_BLEND D3D11BlendState::fromBlend(Blend::Type blend)
{
	switch(blend)
	{
	case Blend::Zero: return D3D11_BLEND_ZERO;
	case Blend::One: return D3D11_BLEND_ONE;
	case Blend::SrcColor: return D3D11_BLEND_SRC_COLOR;
	case Blend::InvSrcColor: return D3D11_BLEND_INV_SRC_COLOR;
	case Blend::SrcAlpha: return D3D11_BLEND_SRC_ALPHA;
	case Blend::InvSrcAlpha: return D3D11_BLEND_INV_SRC_ALPHA;
	case Blend::DestAlpha: return D3D11_BLEND_DEST_ALPHA;
	case Blend::InvDestAlpha: return D3D11_BLEND_INV_DEST_ALPHA;
	case Blend::DestColor: return D3D11_BLEND_DEST_COLOR;
	case Blend::InvDestColor: return D3D11_BLEND_INV_DEST_COLOR;
	case Blend::SrcAlphaSat: return D3D11_BLEND_SRC_ALPHA_SAT;
	case Blend::BlendFactor: return D3D11_BLEND_BLEND_FACTOR;
	case Blend::InvBlendFactor: return D3D11_BLEND_INV_BLEND_FACTOR;
	case Blend::Src1Color: return D3D11_BLEND_SRC1_COLOR;
	case Blend::InvSrc1Color: return D3D11_BLEND_INV_SRC1_COLOR;
	case Blend::Src1Alpha: return D3D11_BLEND_SRC1_ALPHA;
	case Blend::InvSrc1Alpha: return D3D11_BLEND_INV_SRC1_ALPHA;
	default: BOOSTEXT_THROW(invalid_value_exception(blend));
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////

D3D11_BLEND_OP D3D11BlendState::fromBlendOp(BlendOp::Type blendOp)
{
	switch(blendOp)
	{
	case BlendOp::Add:         return D3D11_BLEND_OP_ADD;
	case BlendOp::Subtract:    return D3D11_BLEND_OP_SUBTRACT;
	case BlendOp::RevSubtract: return D3D11_BLEND_OP_REV_SUBTRACT;
	case BlendOp::Min:         return D3D11_BLEND_OP_MIN;
	case BlendOp::Max:         return D3D11_BLEND_OP_MAX;
	default: BOOSTEXT_THROW(invalid_value_exception(blendOp));
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
